package com.cherry.androidcode.struct_interface

import android.text.TextUtils
import android.util.ArrayMap

/**
 * @author DongMS
 * @since 2019/11/27
 */
object FunctionManager {

    private val noParamNoResultMap: ArrayMap<String, FunctionNoParamNoResult?> = ArrayMap()
    private val noParamWithResultMap: ArrayMap<String, FunctionNoParamWithResult<*>> = ArrayMap()
    private val withParamNoResultMap: ArrayMap<String, FunctionWithParamNoResult<*>> = ArrayMap()
    private val withParamWithResultMap: ArrayMap<String, FunctionWithParamWithResult<*, *>> = ArrayMap()

    fun addFunction(function: FunctionNoParamNoResult): FunctionManager {
        noParamNoResultMap[function.functionName] = function
        return this
    }

    fun addFunction(function: FunctionNoParamWithResult<*>): FunctionManager {
        noParamWithResultMap[function.functionName] = function
        return this
    }

    fun addFunction(function: FunctionWithParamNoResult<*>): FunctionManager {
        withParamNoResultMap[function.functionName] = function
        return this
    }

    fun addFunction(function: FunctionWithParamWithResult<*, *>): FunctionManager {
        withParamWithResultMap[function.functionName] = function
        return this
    }

    fun invokeFunc(functionName: String) {
        if (TextUtils.isEmpty(functionName)) {
            return
        }
        val function = noParamNoResultMap[functionName]
        if (function != null) {
            function.function()
        } else {
            throw FunctionException("no this function$functionName")
        }
    }

    fun <Result> invokeFuncForResult(functionName: String, clazz: Class<Result>? = null): Result? {
        if (TextUtils.isEmpty(functionName)) {
            return null
        }
        val function = noParamWithResultMap[functionName]
        return if (function != null) {
            if (clazz == null) {
                function.function() as? Result
            } else {
                clazz.cast(function.function())
            }
        } else {
            throw FunctionException("no this function$functionName")
        }
    }

    fun <Params> invokeFuncForParams(functionName: String, params: Params) {
        if (TextUtils.isEmpty(functionName)) {
            return
        }
        val function = withParamNoResultMap[functionName] as? FunctionWithParamNoResult<Params>
        if (function != null) {
            function.function(params)
        } else {
            throw FunctionException("no this function$functionName")
        }
    }

    fun <Params, Result> invokeFuncForParamsResult(functionName: String, params: Params, clazz: Class<Result>? = null): Result? {
        if (TextUtils.isEmpty(functionName)) {
            return null
        }
        val function = withParamWithResultMap[functionName] as? FunctionWithParamWithResult<Params, Result>
        return if (function != null) {
            if (clazz == null) {
                function.function(params) as? Result
            } else {
                clazz.cast(function.function(params))
            }
        } else {
            throw FunctionException("no this function$functionName")
        }
    }

}